/**
 *	\file AKMediator.m
 *  
 *  \section desc_sec Description
 *  Description
 *  
 *  \if copyright
 *  	Copyright 2008 Ben Chen. All rights reserved.
 *  \endif
 *  
 *  \section hist_sec Change History:
 *  	-	1/19/08		BC		Created this file
 *  
 *  <hr>
 *  \addtogroup AnimalKingdom
 *  @{
 */

#import "AKMediator.h"

#import "AKDrawing.h"

@implementation AKMediator

- (id)initWithBasicInfoProvider:(id <AKBasicInfoProvider>)aBasicInfoProvider
			   geometryProvider:(id <AKGeometryProvider>)aGeometryProvider
				stateMaintainer:(id <AKStateMaintainer>)aStateMaintainer
						players:(NSArray*)newPlayers
				  imageProvider:(id <AKImageProvider>)anImageProvider
					basicObject:(id <AKBasicDrawing>)aBasicDrawingObject
					   drawView:(NSView*)aDrawView
{
	self = [super init];
	
	if (self)
	{
		basicInfoProvider	= [(id)aBasicInfoProvider retain];
		geometryProvider	= [(id)aGeometryProvider retain];
		stateMaintainer		= [(id)aStateMaintainer retain];
		players				= [(id)newPlayers retain];
		imageProvider		= [(id)anImageProvider retain];
		basicDrawing		= [(id)aBasicDrawingObject retain];
		
		drawingView = aDrawView;
	}
	
	return self;
}

- (void) dealloc
{
	[(id)basicInfoProvider	release];
	[(id)geometryProvider	release];
	[(id)stateMaintainer	release];
	[(id)players			release];
	[(id)imageProvider		release];
	[(id)basicDrawing		release];
	
	[super dealloc];
}

#pragma mark Helper Functions
- (void)performMoveForAllPlayers:(AKMove)newMove
{
	for (id <AKPlayer> aPlayer in players)
	{
		if (![aPlayer performMove:newMove])
			[[NSException exceptionWithName:NSInternalInconsistencyException
									 reason:[NSString stringWithFormat:@"AKMediator: player:%@ couldn't perform move:%@", aPlayer, AKStringFromMove(newMove)] 
								   userInfo:nil] raise];
	}
}

- (id <AKPlayer>)playerForSide:(AKSideIndex)nextSide
{
	id <AKPlayer> retVal = nil;
	
	for (id <AKPlayer> aPlayer in players)
	{
		if ([aPlayer side] == nextSide)
		{
			retVal = aPlayer;
			break;
		}
	}
	
	return retVal;
}

- (void)backToInitialState
{
	[basicDrawing removeAllPieces];
	[stateMaintainer backToInitialState];
	
	for (id <AKPlayer> aPlayer in players)
	{
		[aPlayer backToInitialState];
	}
	
	[drawingView setNeedsDisplay:YES];
}

#define BestMove	@"bestMove"

- (void)play
{
	NSArray *winners;
	if ((winners = [stateMaintainer winners]) != nil)
	{
		NSMutableString *msg = [NSMutableString string];
		
		if (basicInfoProvider.numOfSides == [winners count]) {
			[msg appendString:@"Draw!!!"];
		}
		else
		{
			[msg appendString:@"Winner(s):"];
			for (NSNumber *aWinner in winners) {
				[msg appendString:[NSString stringWithFormat:@" Side:%d", [aWinner unsignedIntValue]]];
			}
			[msg appendString:@"."];
		}
		
		if (NSAlertDefaultReturn ==NSRunAlertPanel(@"Game Over", msg, @"Play again", @"Quit", nil))
			[self start];
		else
			[NSApp terminate:self];
	}
	else
	{
		// If no winner yet
		AKSideIndex nextSide = [stateMaintainer nextPlayerSide];
		
		id <AKPlayer> nextPlayer = [self playerForSide:nextSide];
		
		if (nextPlayer)
		{
			[(id)nextPlayer addObserver:self 
							 forKeyPath:BestMove 
								options:NSKeyValueObservingOptionNew
								context:NULL];
			
			[nextPlayer thinkNextMove];
		}
	}
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	if ([keyPath isEqualToString:BestMove])
	{
		// Now the player has came up with a move
		[object removeObserver:self forKeyPath:BestMove];
		
		// We ask the player what is the move
		id <AKPlayer> nextPlayer = (id <AKPlayer>)object;
		AKMove nextMove = [nextPlayer bestMove];
		
		// Then we ask the state maintainer and the player itself to perform this move
		if (![stateMaintainer performMove:nextMove])
			[[NSException exceptionWithName:NSInternalInconsistencyException
									 reason:@"AKMediator:"
								   userInfo:nil] raise];

		[self performMoveForAllPlayers:nextMove];
	
		// Then we present this move
		[basicDrawing addPiece:AKPieceInMove(nextMove, NO)];
		
		[drawingView setNeedsDisplay:YES];
		
		[self play];
	}
}

- (void)start
{	
	[self backToInitialState];
	
	[self play];
}

@end

/** @} */